1 /** 2 * License: 3 * Copyright Devisualization (Richard Andrew Cattermole) 2014 - 2017. 4 * Distributed under the Boost Software License, Version 1.0. 5 * (See accompanying file LICENSE_1_0.txt or copy at 6 * http://www.boost.org/LICENSE_1_0.txt) 7 */ 8 module devisualization.util.algorithms.bunnyhop; 9 import gl3n.linalg : vec3, dot; 10 11 /** 12 * Based upon http://flafla2.github.io/2015/02/14/bunnyhop.html 13 */ 14 struct BunnyHopSpeed { 15 private { 16 vec3 previousVelocity; 17 float airAccel, airMaxAccel, groundAccel, groundMaxAccel; 18 19 ubyte friction; 20 float timeUnit; 21 } 22 23 /** 24 * Base data for a bunny hop calculations 25 * 26 * Params: 27 * friction = The friction of the world. Must be between 1 and 5. Where 5 would cause gliding. 28 * airAccel = Acceleration speed while in the air (e.g. jumps) 29 * airMaxAccel = The maximum acceleration in the air (e.g. jumps) 30 * groundAccel = Acceleration speed while on the ground 31 * groundMaxAccel = The maximum acceleration on the ground 32 * timeUnit = How long is a single time unit aka per frame? Default: 1 33 * previousVelocity = What was the previous velocity that the character was at? Default: X: 0, Y: 0, Z: 0 34 */ 35 this(ubyte friction, float airAccel, float airMaxAccel, float groundAccel, float groundMaxAccel, float timeUnit = 1f, vec3 previousVelocity = vec3(0, 0, 0)) 36 in { 37 assert(friction >= 1 && friction <= 5); 38 } body { 39 this.friction = friction; 40 this.timeUnit = timeUnit; 41 42 this.airAccel = airAccel; 43 this.airMaxAccel = airMaxAccel; 44 this.groundAccel = groundAccel; 45 this.groundMaxAccel = groundMaxAccel; 46 47 this.previousVelocity = previousVelocity; 48 } 49 50 /** 51 * Gets the current velocity and updates it to be the previous one 52 * 53 * Params: 54 * accelDir = The direction the player has specified (e.g. arrow keys) 55 * inAir = Is the player currently in the air? Default: false 56 * 57 * Returns: 58 * The current velocity 59 */ 60 vec3 newVelocity(vec3 accelDir, bool inAir = false) { 61 import std.math : fmax; 62 vec3 ret; 63 64 if (inAir) { 65 ret = accelerate(accelDir, airAccel, airMaxAccel); 66 } else { 67 float speed = cast(float)previousVelocity.magnitude; 68 69 if (speed != 0) { 70 float drop = speed * friction * timeUnit; 71 previousVelocity *= fmax(speed - drop, 0f) / speed; 72 } 73 74 ret = accelerate(accelDir, groundAccel, groundMaxAccel); 75 } 76 77 previousVelocity = ret; 78 return ret; 79 } 80 81 private vec3 accelerate(vec3 accelDir, float accelerate, float max_velocity) { 82 float projVel = dot(previousVelocity, accelDir); 83 float accelVel = accelerate * timeUnit; 84 85 if (projVel + accelVel > max_velocity) 86 accelVel = max_velocity - projVel; 87 88 return previousVelocity + accelDir * accelVel; 89 } 90 }